import { RtpEncodingParameters } from '../../RtpParameters';

export function getRtpEncodings(
	{ offerMediaObject }:
	{ offerMediaObject: any }
): RtpEncodingParameters[]
{
	const ssrcs = new Set();

	for (const line of offerMediaObject.ssrcs || [])
	{
		const ssrc = line.id;

		ssrcs.add(ssrc);
	}

	if (ssrcs.size === 0)
		throw new Error('no a=ssrc lines found');

	const ssrcToRtxSsrc = new Map();

	// First assume RTX is used.
	for (const line of offerMediaObject.ssrcGroups || [])
	{
		if (line.semantics !== 'FID')
			continue;

		let [ ssrc, rtxSsrc ] = line.ssrcs.split(/\s+/);

		ssrc = Number(ssrc);
		rtxSsrc = Number(rtxSsrc);

		if (ssrcs.has(ssrc))
		{
			// Remove both the SSRC and RTX SSRC from the set so later we know that they
			// are already handled.
			ssrcs.delete(ssrc);
			ssrcs.delete(rtxSsrc);

			// Add to the map.
			ssrcToRtxSsrc.set(ssrc, rtxSsrc);
		}
	}

	// If the set of SSRCs is not empty it means that RTX is not being used, so take
	// media SSRCs from there.
	for (const ssrc of ssrcs)
	{
		// Add to the map.
		ssrcToRtxSsrc.set(ssrc, null);
	}

	const encodings: RtpEncodingParameters[] = [];

	for (const [ ssrc, rtxSsrc ] of ssrcToRtxSsrc)
	{
		const encoding: RtpEncodingParameters = { ssrc };

		if (rtxSsrc)
			encoding.rtx = { ssrc: rtxSsrc };

		encodings.push(encoding);
	}

	return encodings;
}

/**
 * Adds multi-ssrc based simulcast into the given SDP media section offer.
 */
export function addLegacySimulcast(
	{
		offerMediaObject,
		numStreams
	}:
	{
		offerMediaObject: any;
		numStreams: number;
	}
): void
{
	if (numStreams <= 1)
		throw new TypeError('numStreams must be greater than 1');

	// Get the SSRC.
	const ssrcMsidLine = (offerMediaObject.ssrcs || [])
		.find((line: any) => line.attribute === 'msid');

	if (!ssrcMsidLine)
		throw new Error('a=ssrc line with msid information not found');

	const [ streamId, trackId ] = ssrcMsidLine.value.split(' ');
	const firstSsrc = ssrcMsidLine.id;
	let firstRtxSsrc;

	// Get the SSRC for RTX.
	(offerMediaObject.ssrcGroups || [])
		.some((line: any) =>
		{
			if (line.semantics !== 'FID')
				return false;

			const ssrcs = line.ssrcs.split(/\s+/);

			if (Number(ssrcs[0]) === firstSsrc)
			{
				firstRtxSsrc = Number(ssrcs[1]);

				return true;
			}
			else
			{
				return false;
			}
		});

	const ssrcCnameLine = offerMediaObject.ssrcs
		.find((line: any) => line.attribute === 'cname');

	if (!ssrcCnameLine)
		throw new Error('a=ssrc line with cname information not found');

	const cname = ssrcCnameLine.value;
	const ssrcs = [];
	const rtxSsrcs = [];

	for (let i = 0; i < numStreams; ++i)
	{
		ssrcs.push(firstSsrc + i);

		if (firstRtxSsrc)
			rtxSsrcs.push(firstRtxSsrc + i);
	}

	offerMediaObject.ssrcGroups = [];
	offerMediaObject.ssrcs = [];

	offerMediaObject.ssrcGroups.push(
		{
			semantics : 'SIM',
			ssrcs     : ssrcs.join(' ')
		});

	for (let i = 0; i < ssrcs.length; ++i)
	{
		const ssrc = ssrcs[i];

		offerMediaObject.ssrcs.push(
			{
				id        : ssrc,
				attribute : 'cname',
				value     : cname
			});

		offerMediaObject.ssrcs.push(
			{
				id        : ssrc,
				attribute : 'msid',
				value     : `${streamId} ${trackId}`
			});
	}

	for (let i = 0; i < rtxSsrcs.length; ++i)
	{
		const ssrc = ssrcs[i];
		const rtxSsrc = rtxSsrcs[i];

		offerMediaObject.ssrcs.push(
			{
				id        : rtxSsrc,
				attribute : 'cname',
				value     : cname
			});

		offerMediaObject.ssrcs.push(
			{
				id        : rtxSsrc,
				attribute : 'msid',
				value     : `${streamId} ${trackId}`
			});

		offerMediaObject.ssrcGroups.push(
			{
				semantics : 'FID',
				ssrcs     : `${ssrc} ${rtxSsrc}`
			});
	}
}
